/*
 * datajoin v1.1 plugin for JQuery
 *
 * Copyright (c) 2011 Blake Lindsay
 * Licensed under the MIT license.
 * http://gotmonkey.com/mit.txt
 */

(function ($) {

    var bindings = [];
	
	function Bind( field, callback ) {
		
		field.bind( 'change.datajoin', callback );
	}

	function LinkBind( obj, property, field, source ) {
	
		obj[property].FieldAssign( field, source );
		
		Bind( field, function( event ) { 
		
			obj[property].SourceAssign( field, source, event ); 
		
		});
		
	}
	
	function TagBind( tagoverride, tag, obj, property, field ){
		
		tagoverride[tag].FieldAssign( field, obj, property );
			
		Bind( field,  function ( event ) { 
		
			tagoverride[tag].SourceAssign( field, obj, property, event ); 
			
		});
		
	}

    function SourceBind( obj, property, field, tagoverride ) {
	
		var tag = field[0].tagName.toLowerCase();
		
		if ( tag in tagoverride ) {
		
			TagBind( tagoverride, tag, obj, property, field );
			
			return;
		}
		
		var callback;
		
		switch ( tag ) {
		
			case 'input' : {
				
				if ( field.is(':checkbox,:radio') ) {
					
					field.attr( 'checked', obj[property] );
					
					callback = function() { obj[property] = field.is(':checked'); };
					
					break;
				
				}				
				
			}
			case 'textarea':
			case 'select': {
				
				field.val( obj[property] );
				
				callback = function() { obj[property] = field.val(); };
				
				break;
				
			}
			default : {
				
				field.text( obj[property] );
				
				callback = function() { obj[property] = field.text(); };
				
			}
			
		}
		
		Bind( field, callback );

	}	
	
	function FindBindingObject( properties, obj ) {
	
		var index = 1;
	
		var prev = 0;
		
		for ( ; index < properties.length; index++, prev++ ) {

            if ( !(properties[index] in obj[ properties[prev] ]) ) {

				return false;

            } 
			
			obj = obj[ properties[prev] ];

        }
		
		return obj;
	}
	
	function BindObject( field, properties, source, objoverride, tagoverride ) {
		
		if ( properties[0] in objoverride ) {

			var linkObj = FindBindingObject( properties, objoverride );
		
			if ( linkObj ) {
				
				LinkBind( linkObj, properties[properties.length - 1], field, source );
				
				return true;
			}
			
        }

		if ( properties[0] in source ) {

			var obj = FindBindingObject( properties, source );
		
			if ( obj ) {
            
				SourceBind( obj, properties[properties.length - 1], field, tagoverride );
				
				return true;
			
			}
			
        }

        return false;

    }
	
	function UnBind ( form, source ) {
		
        $.each( bindings, function ( index ) {

            if ( this.obj === source && form[0] === this.form[0] ) {

				 $.each( this.fields, function () {

					this.unbind( '.datajoin' );

				});

                bindings.splice( index, 1 );

                return false;
            }
        });
		
		return form;
    }

	$.fn.datajoin = function( source, override )
	{
		if ( override === true ) {
			
			return UnBind( this, source );
			
		}
		
		var fields = [];
		
		var objoverride = ( override || {} ).obj || {}; 
		
		var tagoverride = ( override || {} ).tag || {}; 

        this.find( '[data-join]' ).each( function() {
			
			var field = $( this );
			
			var properties = field.attr( 'data-join' ).split('.');
			
			if ( properties.length && BindObject(field, properties, source, objoverride, tagoverride) ) {
			
				fields.push( field );
			
			}
			
		});

        if ( fields.length ) {

            bindings.push( {
                form : this,
                obj : source,
                fields : fields
            });

        }

        return this;
		
	};

})( jQuery );